#include "wm_sdio.h"
#include "wm_cpu.h"
#include "wm_dma.h"
#include "wm_pmu.h"
#include <string.h>
#include "sdio_spi_driver.h"
#include "lcd.h"
// #define USE_PSRAM

#ifdef USE_PSRAM
#include "psram.h"
#endif

#define SDIO_HOST SD

static uint32_t  sdio_spi_dma_buf_size = 0;
static uint32_t *sdio_spi_dma_buf_addr = NULL;
static uint32_t *sdio_spi_dma_temp_buf = NULL;

static volatile bool sdio_spi_dma_ready = true;
static DMA_HandleTypeDef hdma_sdspi_tx;

static void sdio_dma_callback(DMA_HandleTypeDef *hdma);

__attribute__((isr)) void DMA_Channel0_IRQHandler(void)
{
	HAL_DMA_IRQHandler(&hdma_sdspi_tx);
}

void init_sdio_spi_mode()
{
	__HAL_RCC_SDIO_CLK_ENABLE();
	__HAL_RCC_GPIO_CLK_ENABLE();
	__HAL_RCC_DMA_CLK_ENABLE();
	
	HAL_NVIC_SetPriority(DMA_Channel0_IRQn, 0);
	HAL_NVIC_EnableIRQ(DMA_Channel0_IRQn);
	
	__HAL_AFIO_REMAP_SDIO_CLK(LCD_SCL_PORT, LCD_SCL_PIN);  /*CK*/
	__HAL_AFIO_REMAP_SDIO_CMD(LCD_MOSI_PORT, LCD_MOSI_PIN);  /*CMD*/
	
	hdma_sdspi_tx.Instance = DMA_Channel0;
	hdma_sdspi_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
	hdma_sdspi_tx.Init.DestInc = DMA_DINC_DISABLE;
	hdma_sdspi_tx.Init.SrcInc = DMA_SINC_ENABLE;
	hdma_sdspi_tx.Init.DataAlignment = DMA_DATAALIGN_WORD;
	hdma_sdspi_tx.Init.Mode = DMA_MODE_NORMAL_SINGLE;
	hdma_sdspi_tx.Init.RequestSourceSel = DMA_REQUEST_SOURCE_SDIO;
	hdma_sdspi_tx.XferCpltCallback = sdio_dma_callback;

	HAL_DMA_Init(&hdma_sdspi_tx);
	
	CLEAR_BIT(RCC->RST, RCC_RST_SDIOM);
	SET_BIT(RCC->RST, RCC_RST_SDIOM);
	while(READ_BIT(RCC->RST, RCC_RST_SDIOM) == 0);
    wm_sys_clk sysclk;
	SystemClock_Get(&sysclk);

    SDIO_HOST->SEL = 0xC0 | (sysclk.cpuclk / 2 - 1); // enable module, enable mmcclk
    
#if (0) // 时钟频率为系统时钟 1/2
    SDIO_HOST->CR = 0x542 | 0 << 3;// auto transfer, mmc mode. 
#else   // 时钟频率为系统时钟 1/4
    SDIO_HOST->CR = 0x542 | (0b001 << 3); // 001 1/4
    // SDIO_HOST->CR = 0x542 | (0b010 << 3); // 010 1/6
    // SDIO_HOST->CR = 0x542 | (0b011 << 3); // 011 1/8
    // SDIO_HOST->CR = 0x542 | (0b100 << 3); // 100 1/10
    // SDIO_HOST->CR = 0x542 | (0b101 << 3); // 101 1/12
    // SDIO_HOST->CR = 0x542 | (0b110 << 3); // 110 1/14
    // SDIO_HOST->CR = 0x542 | (0b111 << 3); // 111 1/16
#endif
    SDIO_HOST->IM = 0x100; // unmask sdio data interrupt.
    SDIO_HOST->CRCCTL = 0x00; 
    SDIO_HOST->TIMEOUT = 0;
    SDIO_HOST->BYTECNT = 0;
}

void sdio_spi_put(uint8_t d)
{
    SDIO_HOST->BUFCR = 0x4820;
    SDIO_HOST->DATABUF[0] = d;

    SDIO_HOST->BYTECNT = 1;
    SDIO_HOST->IO = 0x01;
    while (1) {
        if ((SDIO_HOST->IO & 0x01) == 0x00)
            break;
    }
}

void write_sdio_spi_dma(uint32_t* data, uint32_t len) 
{
    while(1){
        if ((SDIO_HOST->IO & 0x01) == 0x00)
            break;
    }
    uint32_t offset=0;
    while(len>0){
        int datalen=len;
        if(len>0xfffc)
            datalen=0xfffc;
        len-=datalen;

        SDIO_HOST->BUFCR = 0x4000; //disable dma,
		HAL_DMA_Start(&hdma_sdspi_tx, (uint32_t) (data+offset), (uint32_t)(SDIO_HOST->DATABUF), datalen);
        SDIO_HOST->BUFCR = 0xC20; //enable dma, write sd card
        SDIO_HOST->IF |= 0x7ff; // clear all firstly
        SDIO_HOST->BYTECNT = datalen;
        SDIO_HOST->IO = 0x01;
        offset+=datalen/4;
		HAL_DMA_PollForTransfer(&hdma_sdspi_tx, HAL_DMA_FULL_TRANSFER, HAL_MAX_DELAY);
        while(1){
            if ((SDIO_HOST->BUFCR & 0x400) == 0x00)
                break;
        }
    }
}

static void sdio_dma_callback(DMA_HandleTypeDef *hdma)
{
    // printf("sdio_dma_callback\n");
	// printf("--->buf_size:%d\n", sdio_spi_dma_buf_size);
    if(sdio_spi_dma_buf_size > 0)
    {
        sdio_spi_dma_buf_addr += 65532/4;
        uint32_t bufsize = sdio_spi_dma_buf_size;
        if(bufsize > 65532){
            bufsize = 65532;
        }
        sdio_spi_dma_buf_size -= bufsize;
		HAL_DMA_Start_IT(&hdma_sdspi_tx, (uint32_t)sdio_spi_dma_buf_addr, (uint32_t)(SDIO_HOST->DATABUF), bufsize);
        SDIO_HOST->BUFCR = 0xC20; //enable dma, write sd card
        SDIO_HOST->IF |= 0x7ff; // clear all firstly
        SDIO_HOST->BYTECNT = bufsize;
        SDIO_HOST->IO = 0x01;
    }
    else
    {
#ifdef USE_PSRAM
        dram_heap_free(sdio_spi_dma_temp_buf);
#else
        free(sdio_spi_dma_temp_buf);
#endif
            // 等待信号量 直到上一次DMA数据发送完成
        sdio_spi_dma_ready = true;
        // printf("--->tls_os_sem_release [%s:%d]\r\n",__func__,__LINE__);
    }
}

// 两次数据之间需要加延时，否则会导致屏幕死机，延时时间可以自行调整。
void wait_sdio_spi_dma_ready()
{
    while(!sdio_spi_dma_ready){
        ;
    }
	HAL_Delay(2);
}

void write_sdio_spi_dma_async(uint32_t* data, uint32_t len) 
{
	int temp = len;
    // 等待信号量 直到上一次DMA数据发送完成
	while(sdio_spi_dma_ready == false)
	{
		
	}
    sdio_spi_dma_ready = false;
    // printf("--->%s:%d\r\n",__func__,__LINE__);

    // printf("--->w buf_size:%d\n", len);
    if(len<4){
        printf("send err\n");
        return;
    }

    // if(len%4)
    // {
    //     len -= (len%4); // dma发送长度必须为4的倍数。
    // }
    if(len%4)
    {
        temp += (4 - (len%4)); // dma发送长度必须为4的倍数。
    }
#ifdef USE_PSRAM
    sdio_spi_dma_temp_buf = dram_heap_malloc(temp);
#else
    sdio_spi_dma_temp_buf = malloc(temp);
#endif
    if(sdio_spi_dma_temp_buf == NULL){
        printf("---> malloc sdio_spi_dma_temp_buf err\n");
		sdio_spi_dma_ready = true;
        return;
    }
    memcpy(sdio_spi_dma_temp_buf, data, len);

    sdio_spi_dma_buf_addr = sdio_spi_dma_temp_buf;
    sdio_spi_dma_buf_size = temp;

    while(1){
        if ((SDIO_HOST->IO & 0x01) == 0x00)
            break;
    }

    SDIO_HOST->BUFCR = 0x4000; //disable dma,
    uint32_t bufsize = sdio_spi_dma_buf_size;
    if(bufsize > 65532){
        bufsize = 65532;
    }
    sdio_spi_dma_buf_size -= bufsize;
	HAL_DMA_Start_IT(&hdma_sdspi_tx, (uint32_t)sdio_spi_dma_buf_addr, (uint32_t)(SDIO_HOST->DATABUF), bufsize);

    SDIO_HOST->BUFCR = 0xC20; //enable dma, write sd card
    SDIO_HOST->IF |= 0x7ff; // clear all firstly
    SDIO_HOST->BYTECNT = bufsize;
    SDIO_HOST->IO = 0x01;
}

